const React = require('react');
const ReactNative = require('react-native');
const PropTypes = require('prop-types');
import {ViewPropTypes, TextPropTypes} from 'deprecated-react-native-prop-types';
const createReactClass = require('create-react-class');
const {
    View,
    Animated,
    StyleSheet,
    ScrollView,
    Text,
    Platform,
    Dimensions,
} = ReactNative;
const Button = require('react-native-scrollable-tab-view/Button');

const WINDOW_WIDTH = Dimensions.get('window').width;

const ActiveScrollTab = createReactClass({
    propTypes: {
        goToPage: PropTypes.func,
        activeTab: PropTypes.number,
        tabs: PropTypes.array,
        backgroundColor: PropTypes.string,
        activeTextColor: PropTypes.string,
        inactiveTextColor: PropTypes.string,
        activeFontSize: PropTypes.number,
        inactiveFontSize: PropTypes.number,
        scrollOffset: PropTypes.number,
        style: ViewPropTypes.style,
        tabStyle: ViewPropTypes.style,
        tabsContainerStyle: ViewPropTypes.style,
        textStyle: TextPropTypes.style,
        renderTab: PropTypes.func,
        underlineStyle: ViewPropTypes.style,
        onScroll: PropTypes.func,
        lineWidth: PropTypes.number
    },

    getDefaultProps() {
        return {
            scrollOffset: 52,
            activeTextColor: 'navy',
            inactiveTextColor: 'black',
            backgroundColor: null,
            style: {},
            tabStyle: {},
            tabsContainerStyle: {},
            underlineStyle: {},
            activeFontSize: 14,
            inactiveFontSize:14,
            lineWidth: -1
        };
    },

    getInitialState() {
        this._tabsMeasurements = [];
        this._textMeasurements = [];
        return {
            _leftTabUnderline: new Animated.Value(0),
            _widthTabUnderline: new Animated.Value(0),
            _containerWidth: null,
        };
    },

    componentDidMount() {
      this.props.scrollValue && this.props.scrollValue.addListener(this.updateView);
      requestAnimationFrame(()=>{
        this.updateView({offset: this.props.activeTab});
      })
    },

    updateView(offset) {
        const position = Math.floor(offset.value);
        const pageOffset = offset.value % 1;
        const tabCount = this.props.tabs.length;
        const lastTabPosition = tabCount - 1;

        if (tabCount === 0 || offset.value < 0 || offset.value > lastTabPosition) {
            return;
        }

        if (this.necessarilyMeasurementsCompleted(position, position === lastTabPosition)) {
            this.updateTabPanel(position, pageOffset);
            this.updateTabUnderline(position, pageOffset, tabCount);
        }
    },

    necessarilyMeasurementsCompleted(position, isLastTab) {
        return this._tabsMeasurements[position] &&
            (isLastTab || this._tabsMeasurements[position + 1]) &&
            this._tabContainerMeasurements &&
            this._containerMeasurements;
    },

    updateTabPanel(position, pageOffset) {
        const containerWidth = this._containerMeasurements.width;
        const tabWidth = this._tabsMeasurements[position].width;
        const nextTabMeasurements = this._tabsMeasurements[position + 1];
        const nextTabWidth = nextTabMeasurements && nextTabMeasurements.width || 0;
        const tabOffset = this._tabsMeasurements[position].left;
        const absolutePageOffset = pageOffset * tabWidth;
        let newScrollX = tabOffset + absolutePageOffset;

        // center tab and smooth tab change (for when tabWidth changes a lot between two tabs)
        newScrollX -= (containerWidth - (1 - pageOffset) * tabWidth - pageOffset * nextTabWidth) / 2;
        newScrollX = newScrollX >= 0 ? newScrollX : 0;

        if (Platform.OS === 'android') {
          this._scrollView && this._scrollView.scrollTo({x: newScrollX, y: 0, animated: false, });
        } else {
            const rightBoundScroll = this._tabContainerMeasurements.width - (this._containerMeasurements.width);
            newScrollX = newScrollX > rightBoundScroll ? rightBoundScroll : newScrollX;
            this._scrollView && this._scrollView.scrollTo({x: newScrollX, y: 0, animated: false, });
        }

    },

    updateTabUnderline(position, pageOffset, tabCount) {
        if (this.props.lineWidth < 0){
            try{
                const lineLeft = this._tabsMeasurements[position].left + this._textMeasurements[position].left;
                const lineRight = this._tabsMeasurements[position].right - this._textMeasurements[position].right;
                const lineWidth = this._textMeasurements[position].width;
                let tabWidth;
                if (position < tabCount - 1) {
                    const nextTabLeft = this._tabsMeasurements[position + 1].left + this._textMeasurements[position+1].left;
                    const newLineLeft = (pageOffset * nextTabLeft + (1 - pageOffset) * lineLeft);
                    tabWidth = this._textMeasurements[position+1].width * pageOffset + (1-pageOffset) * lineWidth;
                    this.state._leftTabUnderline.setValue(newLineLeft);
                    this.state._widthTabUnderline.setValue(tabWidth);
                } else {
                    tabWidth = lineRight - lineLeft;
                    this.state._leftTabUnderline.setValue(lineLeft);
                    this.state._widthTabUnderline.setValue(lineWidth);
                }
            }catch (e) {
                console.log(e);
            }
        }
        else {
            try{
                const oldTabWidth = this._tabsMeasurements[position].width;
                const lineLeft = this._tabsMeasurements[position].left + (oldTabWidth - this.props.lineWidth)/2;
                const lineRight = this._tabsMeasurements[position].right - this.props.lineWidth/2;
                const lineWidth = this.props.lineWidth;
                let tabWidth;
                if (position < tabCount - 1) {
                    const nextTabLeft = this._tabsMeasurements[position + 1].left + (oldTabWidth - this.props.lineWidth)/2;
                    const newLineLeft = (pageOffset * nextTabLeft + (1 - pageOffset) * lineLeft);
                    tabWidth = this._tabsMeasurements[position+1].width * pageOffset + (1-pageOffset) * lineWidth;
                    this.state._leftTabUnderline.setValue(newLineLeft);
                    this.state._widthTabUnderline.setValue(tabWidth);
                } else {
                    tabWidth = lineRight - lineLeft;
                    this.state._leftTabUnderline.setValue(lineLeft);
                    this.state._widthTabUnderline.setValue(lineWidth);
                }
            }catch (e) {
                console.log(e);
            }
        }
    },

    renderTab(name, page, isTabActive, onPressHandler, onLayoutHandler, onTextLayoutHandler) {
        const { activeTextColor, inactiveTextColor, textStyle, activeFontSize, inactiveFontSize } = this.props;
        const textColor = isTabActive ? activeTextColor : inactiveTextColor;
        const fontWeight = isTabActive ? 'bold' : 'normal';
        const fontSize = isTabActive ? activeFontSize : inactiveFontSize;
        return <Button
            key={`${name}_${page}`}
            accessible={true}
            accessibilityLabel={name}
            accessibilityTraits='button'
            onPress={() => onPressHandler(page)}
            onLayout={onLayoutHandler}
        >
            <View style={[styles.tab, this.props.tabStyle, ]}>
                <Text style={[{color: textColor, fontWeight, fontSize}, textStyle, ]} onLayout={onTextLayoutHandler}>
                    {name}
                </Text>
            </View>
        </Button>;
    },

    measureTab(page, event) {
        const { x, width, height, } = event.nativeEvent.layout;
        //console.log('layout:', event.nativeEvent.layout);
        this._tabsMeasurements[page] = {left: x, right: x + width, width, height, };
        if (this.props.scrollValue) {
          this.updateView({value: this.props.scrollValue.__getValue(), });  
        }
    },

    measureText(page, event) {
        const { x, width, height, } = event.nativeEvent.layout;
        this._textMeasurements[page] = {left:x, right:x + width, width, height};
    },

    render() {
        const tabUnderlineStyle = {
            position: 'absolute',
            height: 4,
            backgroundColor: 'navy',
            bottom: 0,
        };

        const dynamicTabUnderline = {
            left: this.state._leftTabUnderline,
            width: this.state._widthTabUnderline,
        };

        return <View
            style={[styles.container, {backgroundColor: this.props.backgroundColor, }, this.props.style, ]}
            onLayout={this.onContainerLayout}
        >
            <ScrollView
                ref={(scrollView) => { this._scrollView = scrollView; }}
                horizontal={true}
                showsHorizontalScrollIndicator={false}
                showsVerticalScrollIndicator={false}
                directionalLockEnabled={true}
                bounces={false}
                scrollsToTop={false}
            >
                <View
                    style={[styles.tabs, {width: this.state._containerWidth, }, this.props.tabsContainerStyle, ]}
                    ref={'tabContainer'}
                    onLayout={this.onTabContainerLayout}
                >
                    {this.props.tabs.map((name, page) => {
                        const isTabActive = this.props.activeTab === page;
                        const renderTab = this.props.renderTab || this.renderTab;
                        return renderTab(name, page, isTabActive, this.props.goToPage, this.measureTab.bind(this, page), this.measureText.bind(this, page));
                    })}
                    <Animated.View style={[tabUnderlineStyle, dynamicTabUnderline, this.props.underlineStyle, ]} />
                </View>
            </ScrollView>
        </View>;
    },

    componentWillReceiveProps(nextProps) {
        // If the tabs change, force the width of the tabs container to be recalculated
        if (JSON.stringify(this.props.tabs) !== JSON.stringify(nextProps.tabs) && this.state._containerWidth) {
            this.setState({ _containerWidth: null, });
        }
    },

    onTabContainerLayout(e) {
        this._tabContainerMeasurements = e.nativeEvent.layout;
        let width = this._tabContainerMeasurements.width;
        if (width < WINDOW_WIDTH) {
            width = WINDOW_WIDTH;
        }
        this.setState({ _containerWidth: width, });
        if (this.props.scrollValue) {
          this.updateView({value: this.props.scrollValue.__getValue(), });  
        }
    },

    onContainerLayout(e) {
        this._containerMeasurements = e.nativeEvent.layout;
        if (this.props.scrollValue) {
          this.updateView({value: this.props.scrollValue.__getValue(), });  
        }
    },
});

module.exports = ActiveScrollTab;

const styles = StyleSheet.create({
    tab: {
        height: 45,
        alignItems: 'center',
        justifyContent: 'center',
        paddingLeft: 20,
        paddingRight: 20,
    },
    container: {
        height: 50,
    },
    tabs: {
        flexDirection: 'row',
        justifyContent: 'space-around',
    },
});
